package cn.net.dpi.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMethod;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Slf4j
public final class HttpUtil {

    private HttpUtil() {
    }

    //默认1分钟超时
    private static final int TIME_OUT = 1;

    private static final OkHttpClient CLIENT = new OkHttpClient.Builder().
            connectTimeout(TIME_OUT, TimeUnit.MINUTES).readTimeout(TIME_OUT, TimeUnit.MINUTES).writeTimeout(TIME_OUT, TimeUnit.MINUTES).build();

    /**
     * post json请求
     *
     * @param url   url
     * @param param 参数
     * @return  Object
     */
    public static Object postJson(String url, Object param) {
        return postJson(url, param, null, null, null);
    }

    /**
     * post json请求
     *
     * @param url   url
     * @param param 参数
     * @return  Object
     */
    public static Object postJson(String url, Object param, Function<String, Object> resultParse) {
        return postJson(url, param, null, null, resultParse);
    }

    /**
     * post json请求  带header
     *
     * @param url   url
     * @param param 参数
     * @return  Object
     */
    public static Object postJson(String url, Object param, Map<String, String> headers) {
        return postJson(url, param, headers, null, null);
    }

    /**
     * post json请求
     *
     * @param url         url
     * @param param       参数
     * @param preParse    请求参数解析(加密等)
     * @param resultParse 返回参数解析(解密or对象解析)
     * @return  Object
     */
    public static Object postJson(String url, Object param, Map<String, String> headers, Function<Object, Object> preParse, Function<String, Object> resultParse) {
        return httpRequest(RequestMethod.POST, url, param, headers, preParse, resultParse, CLIENT);
    }

    /**
     * get json请求
     *
     * @param url   url
     * @return  Object
     */
    public static Object getJson(String url) {
        return getJson(url, null, null, null, null);
    }/**
     * get json请求
     *
     * @param url   url
     * @param param 参数
     * @return  Object
     */
    public static Object getJson(String url, Object param) {
        return getJson(url, param, null, null, null);
    }

    /**
     * get json请求 带header
     *
     * @param url   url
     * @param param 参数
     * @param headers   headers
     * @return  Object
     */
    public static Object getJson(String url, Object param, Map<String, String> headers) {
        return getJson(url, param, headers, null, null);
    }

    /**
     * get json请求
     *
     * @param url         url
     * @param param       参数
     * @param resultParse 返回参数解析(解密or对象解析)
     * @return  Object
     */
    public static Object getJson(String url, Object param, Function<String, Object> resultParse) {
        return getJson(url, param, null, null, resultParse);
    }


    /**
     * get json请求
     *
     * @param url         url
     * @param param       参数
     * @param preParse    请求参数解析(加密等)
     * @param resultParse 返回参数解析(解密or对象解析)
     * @return  Object
     */
    public static Object getJson(String url, Object param, Map<String, String> headers, Function<Object, Object> preParse, Function<String, Object> resultParse) {
        return httpRequest(RequestMethod.GET, url, param, headers, preParse, resultParse, CLIENT);
    }

    /**
     * delete json请求
     *
     * @param url   url
     * @return  Object
     */
    public static Object delJson(String url) {
        return delJson(url, null, null, null, null);
    }

    /**
     * delete json请求
     *
     * @param url   api
     * @param param 参数
     * @return  Object
     */
    public static Object delJson(String url, Object param) {
        return delJson(url, param, null, null, null);
    }

    /**
     * get json请求
     *
     * @param url         url
     * @param param       参数
     * @param preParse    请求参数解析(加密等)
     * @param resultParse 返回参数解析(解密or对象解析)
     * @return  Object
     */
    public static Object delJson(String url, Object param, Map<String, String> headers, Function<Object, Object> preParse, Function<String, Object> resultParse) {
        return httpRequest(RequestMethod.DELETE, url, param, headers, preParse, resultParse, CLIENT);
    }
    /**
     * http请求
     *
     * @param type 请求类型
     * @param url   url
     * @param param 参数
     * @param headers   请求头
     * @param preParse  前置解析
     * @param resultParse 后置解析
     * @return  Object
     */
    public static Object httpRequest(RequestMethod type, String url, Object param, Map<String, String> headers,
                                     Function<Object, Object> preParse, Function<String, Object> resultParse, OkHttpClient client) {
        log.info("{}请求开始 api:{}  params:{} headers:{}", type.toString(), url, param, headers);
        String logStr = "";
        try {
            if (null != preParse) {
                param = preParse.apply(param);
            }
            Request request = buildRequest(type, url, param, headers);
            Response execute = client.newCall(request).execute();
            String result = execute.body() == null ? "" : execute.body().string();
            logStr = execute.toString() + " result: " + result;
            log.info("Http请求完成 api:{} {}", url, logStr);
            if (!execute.isSuccessful()) {
                throw new RuntimeException("Http请求失败 result:" + result);
            }
            return resultParse == null ? result : resultParse.apply(result);
        } catch (Exception e) {
            log.error("Http请求异常", e);
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 根据类型创建请求对象
     *
     * @param type  请求类型
     * @param url   url
     * @param param 参数
     * @param headers 请求头
     * @return  Request
     */
    private static Request buildRequest(RequestMethod type, String url, Object param, Map<String, String> headers) throws Exception {
        Request.Builder builder = new Request.Builder();
        if (!CollectionUtils.isEmpty(headers)) {
            builder.headers(Headers.of(headers));
        }
        //fromdata
        if (RequestMethod.PATCH == type) {
            FormBody.Builder b = new FormBody.Builder();
            Map<String, String> params = (Map<String, String>) param;
            for (Map.Entry<String, String> entry : params.entrySet()) {
                b.add(entry.getKey(), entry.getValue());
            }
            FormBody formBody = b.build();
            return builder.url(url).post(formBody).build();
        } else if (RequestMethod.GET == type) {
            return builder.url(initUriPathParams(url, param)).get().build();
        } else if (RequestMethod.DELETE == type) {
            return builder.url(initUriPathParams(url, param)).delete().build();
        } else {
            String reqBody = param + "";
            if (!(param instanceof String)) {
                reqBody = JSONObject.toJSONString(param);
            }
            RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
            return builder.url(url).post(requestBody).build();
        }
    }

    /**
     * 拼接get url
     *
     * @param url  url
     * @param param 参数
     * @return  String
     */
    private static String initUriPathParams(String url, Object param) throws UnsupportedEncodingException {
        String key;
        String value;
        StringBuilder builder = new StringBuilder();
        builder.append(url);
        List<String> params = new ArrayList<>();
        if (null != param) {
            Map<String, Object> innerMap = JSON.parseObject(JSON.toJSONString(param)).getInnerMap();
            builder.append("?");
            for (Map.Entry<String, Object> entry : innerMap.entrySet()) {
                key = entry.getKey();
                value = entry.getValue() == null ? "" : entry.getValue().toString();
                String tmpKv = String.format(Locale.ROOT, "%s=%s", URLEncoder.encode(key, "UTF-8"), URLEncoder.encode(value, "UTF-8"));
                params.add(tmpKv);
            }
        }
        return builder.append(String.join("&", params)).toString();
    }

    /**
     * from表单请求
     *
     * @param loginUrl Url
     * @param params 参数
     */
    public static Object postFrom(String loginUrl, Map<String, String> params, Function<String, Object> resultParse) {
        return httpRequest(RequestMethod.PATCH, loginUrl, params, null, null, resultParse, CLIENT);
    }
}
